/*
 * Copyright 2008 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.ccsoft.mc.core.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.ccsoft.mc.core.Interceptor;
import org.ccsoft.mc.core.Property;
import org.ccsoft.mc.core.Service;
import org.ccsoft.mc.core.ServiceBundle;
import org.ccsoft.mc.core.ServiceChangeListenerProcessor;
import org.ccsoft.mc.core.ServiceContext;
import org.ccsoft.mc.core.ServiceContextAwareProcessor;
import org.ccsoft.mc.core.ServiceProcessException;
import org.ccsoft.mc.core.ServiceProcessInterceptor;
import org.ccsoft.mc.core.exceptions.ServiceProcessInterceptorException;
import org.ccsoft.mc.core.exceptions.LoadConfigException;
import org.ccsoft.mc.core.interceptor.impl.ServiceInterceptorProxy;
import org.ccsoft.mc.core.exceptions.MiniContainerException;
/**
 * 
 * @author Chao Cai
 * 
 * 
 *
 */
public class MiniContainer {
	private ServiceContext serviceContext;
	private BaseCache readyServiceCache;
	private BaseCache unresolvingServiceCache;	
	private Set<ServiceProcessInterceptor> interceptors
											=Collections.synchronizedSet(new HashSet<ServiceProcessInterceptor>());
	public void addServiceProcessInterceptor(ServiceProcessInterceptor interceptor){
		try {
			processInterceptors(interceptor);
		} catch (ServiceProcessInterceptorException e) {
			throw new ServiceProcessException(e);
		}
		interceptors.add(interceptor);
	}
	private void processInterceptors(Object service) throws ServiceProcessInterceptorException{
		for (ServiceProcessInterceptor interceptor:interceptors){
			interceptor.processService(service);
		}
	}
	private void initDefaultInterceptors(){
		ServiceContextAwareProcessor contextInterceptor=new ServiceContextAwareProcessor(this.serviceContext);
		this.addServiceProcessInterceptor(contextInterceptor);
		ServiceChangeListenerProcessor listenerInterceptor=new ServiceChangeListenerProcessor();
		this.addServiceProcessInterceptor(listenerInterceptor);
	}
	public MiniContainer(){
		readyServiceCache= new BaseCache();
		unresolvingServiceCache=new BaseCache();
		DependencyManager dm =new DependencyManager();		
		serviceContext=new ServiceContextImpl(readyServiceCache,unresolvingServiceCache);
		initDefaultInterceptors();
		try {
			this.processInterceptors(dm);
		} catch (ServiceProcessInterceptorException e) {
			throw new MiniContainerException("Construct DependencyManager failed");
		}
	}
	public void addBundles(List<ServiceBundle> bundles){
		for (ServiceBundle bundle:bundles){
			this.addBundle(bundle);
		}
	}
	public void loadBundlesFromFilePath(String fileName) {			
			ConfigParser parser=new ConfigParser();
			parser.validate(getConfigFromFilePath(fileName));
			parser.loadConfig(getConfigFromFilePath(fileName));
			List<ServiceBundle> bundles=parser.getBundles();
			addBundles(bundles);		
	}
	private InputStream getConfigFromClassPath(String fileName){
		 InputStream  is = ClassLoader.getSystemResourceAsStream(fileName);	
		 return is;
	}
	private InputStream getConfigFromFilePath(String fileName){
		InputStream fis=null;
		try {
			fis = new FileInputStream(new File(fileName));
		} catch (FileNotFoundException e) {
			throw new LoadConfigException(e);
		}
		 return fis;
	}
	public void loadBundlesFromClassPath(String fileName){		    	     
	         ConfigParser parser=new ConfigParser();
	         parser.validate(getConfigFromClassPath(fileName));
	         parser.loadConfig(getConfigFromClassPath(fileName));	         
	         List<ServiceBundle> bundles=parser.getBundles();
	         addBundles(bundles);
	}
	private void injectPropertys(ServiceBundle bundle){
		for (Property prop:bundle.getProperties()){
			BundleUtil.setProperty(bundle.getInstance(),prop);
		}
	}
	private void insertInterceptor(ServiceBundle bundle){
		for (Service service:bundle.getServices()){
			for (String interceptor:service.getInterceptors()){
				Interceptor ins=null;
				try {
					ins = (Interceptor) Class.forName(interceptor).newInstance();
				} catch (Exception e) {
					throw new  LoadConfigException("Interceptor :"+interceptor,e);
				}
				Object obj=ServiceInterceptorProxy.newInstance(bundle.getInstance(),service.getInterfaceName(),ins);
				bundle.setInstance(obj);
			}
		}
	}
	public void addBundle(ServiceBundle bundle){
		injectPropertys(bundle);
		insertInterceptor(bundle);
		try {
			this.processInterceptors(bundle.getInstance());
		} catch (ServiceProcessInterceptorException e) {
			throw new ServiceProcessException(e);
		}
		if (BundleUtil.isAllReferenceAvailable(bundle,readyServiceCache)){
			BundleUtil.injectReference(bundle,readyServiceCache);			
			readyServiceCache.add(bundle);
			unresolvingServiceCache.remove(bundle);
		}else{
			unresolvingServiceCache.add(bundle);
		}
	}
	public ServiceContext getServiceContext(){
		return serviceContext;
	}
	

}
